package com.jacky.demo.widget.camera;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.FragmentManager;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.MeteringRectangle;
import android.media.ExifInterface;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Size;
import android.view.MotionEvent;
import android.view.Surface;

import com.jacky.demo.util.StorageUtil;
import com.jacky.log.Logger;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Created by lixinquan on 2020/4/9.
 */
class CameraUtils {
    private static final double RATIO_4_3_VALUE = 4.0 / 3.0;
    private static final double RATIO_16_9_VALUE = 16.0 / 9.0;
    /**
     * 检测宽高比是4:3 还是 16:9
     */
    static boolean aspectRatio(int width, int height) {
        double previewRatio = Math.max((double) width, height) / Math.min(width, height);
        return Math.abs(previewRatio - RATIO_4_3_VALUE) <= Math.abs(previewRatio - RATIO_16_9_VALUE);
    }

    /**
     * Return true if the given array contains the given integer.
     * @return true if the array contains the given integer, otherwise false.
     */
    static boolean contains(CameraCharacteristics characteristics,CameraCharacteristics.Key<int[]> key, int mode) {
        if(characteristics == null) return false;
        int[] modes = characteristics.get(key);
        if (modes == null) {
            return false;
        }
        for (int i : modes) {
            if (i == mode) {
                return true;
            }
        }
        return false;
    }

    public static <T extends AutoCloseable> T close(T closeable) {
        if(closeable != null) {
            try {
                closeable.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * Rotation need to transform from the camera sensor orientation to the device's current
     * orientation.
     *
     * @param c                 the {@link CameraCharacteristics} to query for the camera sensor
     *                          orientation.
     * @param deviceOrientation the current device orientation relative to the native device
     *                          orientation.
     * @return the total rotation from the sensor orientation to the current device orientation.
     */
    static int sensorToDeviceRotation(CameraCharacteristics c, int deviceOrientation) {
        Integer sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
        sensorOrientation = (sensorOrientation == null) ? 0 : sensorOrientation;
        // Get device orientation in degrees
        deviceOrientation = getRotation(deviceOrientation);
        // Reverse device orientation for front-facing cameras
        Integer face = c.get(CameraCharacteristics.LENS_FACING);
        face = face == null ? 0 : face;
        if (face == CameraCharacteristics.LENS_FACING_FRONT) {
            deviceOrientation = -deviceOrientation;
        }
        return (sensorOrientation + deviceOrientation + 360) % 360;
    }

    private static int getRotation(int deviceOrientation) {
        switch (deviceOrientation) {
            case Surface.ROTATION_0 : return 0;
            case Surface.ROTATION_90 : return 90;
            case Surface.ROTATION_180 : return 180;
            case Surface.ROTATION_270 : return 270;
        }
        return 0;
    }

    private static int getDeviceRotation(int orientation) {
        switch (orientation) {
            case 90 : return ExifInterface.ORIENTATION_ROTATE_90;
            case 180 : return ExifInterface.ORIENTATION_ROTATE_180;
            case 270 : return ExifInterface.ORIENTATION_ROTATE_270;
        }
        return ExifInterface.ORIENTATION_NORMAL;
    }

    static int getActivityBrightness(Context context) {
        int screenBrightness = -1;
        if(context == null) return screenBrightness;
        try {
            screenBrightness = Settings.System.getInt(context.getContentResolver(),
                    Settings.System.SCREEN_BRIGHTNESS);
        } catch (Settings.SettingNotFoundException e) {
            e.printStackTrace();
        }
        return screenBrightness;
    }

    static void focusOnTouch(
            CaptureRequest.Builder request, CameraCharacteristics characteristics,float x, float y, boolean aFRun) {
//        Rect cropRegion;
//        { //cropRegionForZoom
//            Rect sensor = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
//            if(sensor == null) return;
////            int xCenter = sensor.width() / 2;
////            int yCenter = sensor.height() / 2;
////            int xDelta = (int) (0.5f * sensor.width() * zoom);
////            int yDelta = (int) (0.5f * sensor.height() * zoom);
////            cropRegion = new Rect(xCenter - xDelta, yCenter - yDelta, xCenter + xDelta, yCenter + yDelta);
//            cropRegion = sensor;
//        }
//        MeteringRectangle[] mAFRegions = regionsForNormalizedCoord((int)x, (int)y, cropRegion);
//
//        if(aFRun) {
//            request.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
//            request.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
//            request.set(CaptureRequest.CONTROL_AF_REGIONS, mAFRegions);
//        }
//        request.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START);
//        request.set(CaptureRequest.CONTROL_AE_REGIONS, mAFRegions);
    }

    private static MeteringRectangle[] regionsForNormalizedCoord(int nx, int ny, final Rect cropRegion) {
        int h = 300;
        Rect meteringRegion = new Rect(ny - h, nx - h, ny + h, nx + h);

        // Clamp meteringRegion to cropRegion.
        meteringRegion.left = clamp(meteringRegion.left, cropRegion.left, cropRegion.right);
        meteringRegion.top = clamp(meteringRegion.top, cropRegion.top, cropRegion.bottom);
        meteringRegion.right = clamp(meteringRegion.right, cropRegion.left, cropRegion.right);
        meteringRegion.bottom = clamp(meteringRegion.bottom, cropRegion.top, cropRegion.bottom);
        return new MeteringRectangle[]{new MeteringRectangle(meteringRegion, 1000)};
    }
    private static int clamp(int x, int min, int max) {
        return x > max ? max : Math.max(x, min);
    }
    static boolean setup3AControlsLocked(CaptureRequest.Builder builder, CameraCharacteristics characteristics) {
        // Enable auto-magical 3A run by camera device
        builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
        Float minFocusDist = characteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);
        // If MINIMUM_FOCUS_DISTANCE is 0, lens is fixed-focus and we need to skip the AF run.
        boolean mAFRun = !(minFocusDist == null || minFocusDist == 0);
        if (mAFRun) {
            //AF自动对焦
            if (contains(characteristics, CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)) {
                builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            } else {
                builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
            }
        }
        //AE自动曝光
        builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
        //白平衡
        if (contains(characteristics, CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES, CaptureRequest.CONTROL_AWB_MODE_AUTO)) {
            builder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO);
        }
        return mAFRun;
    }
    static void saveImage(byte[] data, int rotation, File file) {
        Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length);

        Logger.d("rotation", rotation);
        boolean isOOm = false;
        if (rotation != 0) {
            try {
                Matrix m = new Matrix();
                m.setRotate(rotation);
                Bitmap bm2 = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), m, true);
                bm.recycle();
                bm = bm2;
            } catch (OutOfMemoryError e) {
                Logger.e("createBitmap error...");
                isOOm = true;
            }
        }
        if(isOOm) { //因为内存溢出，，所以存原数据内容，有旋转角度
            bm.recycle();
            FileOutputStream stream = null;
            try {
                stream = new FileOutputStream(file);
                stream.write(data);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                StorageUtil.close(stream);
            }
            try {
                ExifInterface exifInterface = new ExifInterface(file.getAbsolutePath());
                int r = getDeviceRotation(rotation);
                exifInterface.setAttribute(ExifInterface.TAG_ORIENTATION, String.valueOf(r));
                exifInterface.saveAttributes();
            } catch (IOException e) {
                Logger.e(e);
            }
        } else {
            StorageUtil.saveImage(bm, file);
            bm.recycle();
        }
    }

    static void buildErrorDialog(FragmentManager manager, String errorMessage) {
        ErrorDialog dialog = new ErrorDialog();
        dialog.mErrorMessage = errorMessage;
        dialog.show(manager, "dialog");
    }

    /**
     * A dialog fragment for displaying non-recoverable errors; this {@ling Activity} will be
     * finished once the dialog has been acknowledged by the user.
     */
    public static class ErrorDialog extends DialogFragment {

        private String mErrorMessage;
        public ErrorDialog() {
            mErrorMessage = "Unknown error occurred!";
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final Activity activity = getActivity();
            return new AlertDialog.Builder(activity)
                    .setMessage(mErrorMessage)
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            activity.finish();
                        }
                    })
                    .create();
        }
    }

    static Size findFitSize(boolean isRatio43, Size[] sizes) {
        if(sizes == null) throw new IllegalStateException("not found size for jpg");
        Size maxArea = null;
        for(Size size : sizes) {
            if(isRatio43 != CameraUtils.aspectRatio(size.getWidth(), size.getHeight())) continue;
            if(maxArea == null) maxArea = size;
            else {
                if(maxArea.getWidth() < size.getWidth() && maxArea.getHeight() < size.getHeight()) maxArea = size;
            }
        }
        return maxArea == null ? sizes[0] : maxArea;
    }

    static double distance(MotionEvent event) {
        float x1 = event.getX(0);
        float y1 = event.getY(0);
        float x2 = event.getX(1);
        float y2 = event.getY(1);
        float x = x1 - x2;
        float y = y1 - y2;
        return Math.sqrt(x * x + y * y);
    }
}
